import * as THREE from "../../../modules/three/three.module.js";
import {interpolatePoints} from "../lib/util/StaticMethods.js";
import {WorldConfig} from "../lib/CommonConsts.js";
import Cartesian3 from "../lib/cesium/Core/Cartesian3.js";
import Ellipsoid from "../lib/cesium/Core/Ellipsoid.js";
import Cartographic from "../lib/cesium/Core/Cartographic.js";

const unitZ = new THREE.Vector3(0, 0, 1)
const tmp = new THREE.Vector3(0, 0, 1)
// "nozzleDirection":-1 # (int)喷嘴方向 0:复位，1:↑，2:↓，3:←,4:→, 默认为-1，不执行转喷头动作
export const NozzleActionEnum = {
  reset: 0, // 喷头复位
  up: 1, // 喷头向上转
  down: 2, // 喷头向下转
  left: 3, // 喷头向左转
  right: 4, // 喷头向右转
  keep: -1, // 喷头不执行动作，保持原样
};
const readableNozzleAction = ['reset', 'up', 'down', 'left', 'right'];
const getReadableNozzleAction = (action) => {
  if (action < 0) {
    return 'keep';
  } else if (action >= readableNozzleAction.length) {
    return 'unknown';
  }
  return readableNozzleAction[action];
};

// 负责渲染航线喷涂动画中的一帧
class SprayPathAnimateItem {
  constructor(order, worldPos, workArea) {
    this.order = order
    this.droneWorldPos.copy(worldPos)
    this.workArea = workArea
  }

  /**
   * 整型，在动画中的序号
   * @type {number}
   */
  order
  /**
   * 该动画帧正在对哪个区域进行喷涂
   * @type {WorkArea | undefined}
   */
  workArea
  /**
   * 位于 归一化的 以canvas左上角为原点的 本地坐标系；每个工作区都对应一个纹理贴图
   * @type {THREE.Box3 | undefined}
   */
  box
  /**
   * 无人机的世界坐标
   * @type {THREE.Vector3}
   */
  droneWorldPos = new THREE.Vector3()
  /**
   * 有 AI 计算出来的，当前位置是否需要喷涂
   * @type {boolean}
   */
  spraying = false
}

// 对应 AI 规划出来的一条无人机航线数据
export default class SprayPath {
  distanceToWall = 0.8
  /**
   * @type {number[]}
   */
  workAreaIds = []
  /**
   * @type {THREE.Vector3}
   */
  facing
  cssColorStr
  /**
   * @type {THREE.Color}
   */
  color
  /**
   * AI规划出来的航道坐标序列，位于世界坐标系
   * @type {THREE.Vector3[]}
   */
  wallPath = []
  dronePath = []
  /**
   * AI规划出来的航道坐标序列，在当前位置是否需要喷涂
   * @type {boolean[]}
   */
  _sprayFlags = []
  nozzleActions = []
  needSprays = []
  /**
   * 对 dronePath进行插值后的序列，位于世界坐标系
   * @type {THREE.Vector3[]}
   */
  interpolatedDronePath = []
  interpolatedSprayFlags = []
  /**
   * @type {THREE.Box3}
   */
  boundingBox
  /**
   * 无人机喷涂一次，覆盖的宽度 和 高度，可以修改
   * @type {{w: number, h: number}}
   */
  editableRect = {w: 1.0, h: 0.5}

  /**
   * 根据 python AI 给出的数据来初始化
   * @param {object} jsonObj
   * @return {SprayPath}
   */
  fromJson(jsonObj) {
    this.cssColorStr = '#' + jsonObj.color;
    this.color = new THREE.Color(this.cssColorStr);
    this.workAreaIds = jsonObj.id;
    this.facing = new THREE.Vector3().fromArray(jsonObj.facing);
    this.wallPath.length = 0;
    this.dronePath.length = 0;
    this._sprayFlags.length = 0;
    const offset = this.facing.clone().multiplyScalar(this.distanceToWall);
    jsonObj.taskJson.forEach(arr => {
      const [coordArr, needSpray, nozzleAction] = arr;
      const coord = new THREE.Vector3().fromArray(coordArr);
      this.wallPath.push(coord.clone());
      coord.add(offset);
      this.dronePath.push(coord);
      this._sprayFlags.push(needSpray === 1 ? true : false);
      this.needSprays.push(needSpray);
      this.nozzleActions.push(nozzleAction);
    });
    this.interpolatedDronePath = this.dronePath;
    this.boundingBox = new THREE.Box3().setFromPoints(this.dronePath);
    return this;
  }

  // 保存到本地local storage
  toStorageJson() {
    const color = this.cssColorStr;
    const workAreaIds = this.workAreaIds;
    const facing = this.facing.toArray([]);
    const distanceToWall = this.distanceToWall;
    const wallPath = [];
    this.wallPath.forEach(point => {
      wallPath.push(point.x, point.y, point.z)
    });
    const rect = this.editableRect;
    return {color, workAreaIds, facing, distanceToWall, wallPath, rect};
  }

  // 从本地local storage 加载
  fromStorageJson(jsonObj) {
    const {color, workAreaIds, facing, distanceToWall, wallPath} = jsonObj;
    this.cssColorStr = color;
    this.color = new THREE.Color().setStyle(color);
    this.workAreaIds = workAreaIds;
    this.facing = new THREE.Vector3().fromArray(facing);
    this.distanceToWall = Math.max(distanceToWall, WorldConfig.spray_to_plane_distance);
    if (jsonObj.rect) {
      this.editableRect = jsonObj.rect;
    }
    const offset = this.facing.clone().multiplyScalar(this.distanceToWall);
    for (let i = 0; i < wallPath.length; i += 3) {
      const wallPos = new THREE.Vector3(wallPath[i], wallPath[i + 1], wallPath[i + 2]);
      const dronePos = wallPos.clone().add(offset);
      this.wallPath.push(wallPos);
      this.dronePath.push(dronePos);
      this._sprayFlags.push(true);
    }
    return this;
  }

  setPenRect(array) {
    this.editableRect.w = array[0];
    this.editableRect.h = array[1];
    return this;
  }
  retrieveSprayAndUnSpraySegments() {
    const sprayArr = [];
    const unsprayArr = [];
    const arr = this.dronePath;
    for (let i = 1; i < this._sprayFlags.length; i++) {
      // 线段的后一个端点，决定当前线段是否喷涂
      const segment = [arr[i - 1], arr[i]];
      const spraying = this._sprayFlags[i];
      if (spraying) {
        sprayArr.push(...segment);
      } else {
        unsprayArr.push(...segment);
      }
    }
    return [sprayArr, unsprayArr];
  }

  getTipStrByIndices(indices, toEarthMat) {
    let tipStr = '';
    indices.forEach((num0, index) => {
      const id = num0 + 1;
      // const zH = this.dronePath[num0].z.toFixed(3)
      tmp.copy(this.dronePath[num0]).applyMatrix4(toEarthMat);
      const c3 = new Cartesian3(tmp.x, tmp.y, tmp.z);
      const cart = Ellipsoid.WGS84.cartesianToCartographic(c3, new Cartographic());
      const zH = cart.height.toFixed(3)
      // const num = id >= this.nozzleActions.length ? this.nozzleActions.length - 1 : id;
      const str = `{waypoint${id}, z: ${zH}, nozzle: ${getReadableNozzleAction(
        this.nozzleActions[num0],
      )}, ${this.needSprays[num0] ? 'spraying' : 'no spray'}}`;
      tipStr += str;
      if (index < indices.length - 1) {
        tipStr += '\n';
      }
    });
    return tipStr;
  }

  /**
   * 由于 AI 生成的航点可能不是匀速的，所以航点与航点之间可能需要插值，根据无人机的飞行速度进行插值
   */
  interpolateCoords() {
    const target = [this.dronePath[0]];
    const sprayFlags = [this._sprayFlags[0]];
    const speed = WorldConfig.spray_fly_per_frame;
    this.dronePath.forEach((coord, index) => {
      if (index === this.dronePath.length - 1) {
        return;
      }
      const nextCoord = this.dronePath[index + 1];
      const coords = interpolatePoints(coord, nextCoord, speed);

      coords.shift();
      target.push(...coords);
      const nextSprayFlag = this._sprayFlags[index + 1];
      if (nextSprayFlag) {
        coords.forEach(it => sprayFlags.push(true));
      } else {
        coords.forEach(it => sprayFlags.push(false));
      }
    })
    this.interpolatedDronePath = target;
    this.interpolatedSprayFlags = sprayFlags;
    return target
  }

  /**
   * 为无人机 航线动画的播放准备数据
   * @param {WorkArea[]} workAreas
   * @param {number} w 一次喷漆 可以覆盖到墙面的宽度，以米为单位
   * @param {number} h 一次喷漆 可以覆盖到墙面的高度，以米为单位
   * @return {SprayPathAnimateItem[]}
   */
  createAnimateItems(workAreaArr, w, h) {
    const workAreas = this.workAreaIds.map(id => workAreaArr.find(workArea => workArea.id === id))
    if (workAreas.find(it => !it)) {
      console.warn("无人机航线 对应的工作区id 中包含无效id, 航线对应的若干工作区中有些已经被删除了", this.workAreaIds)
      // return []
    }

    const target = []
    this.interpolatedDronePath.forEach((coord, index) => {
      const workArea = workAreaArr.find(workArea => workArea.intersectsWithSprayRect(coord, w, h))
      const item = new SprayPathAnimateItem(index, coord, workArea)
      if (workArea) {
        const localPos = coord.clone().applyMatrix4(workArea.planeMatInv)
        const rectCenter = workArea.localBox3.getCenter(new THREE.Vector3())
        const rectSize = workArea.localBox3.getSize(new THREE.Vector3())
        const size = rectSize.x > rectSize.y ? rectSize.x : rectSize.y
        // pos： 一次喷涂的中心相对于工作区中心的坐标  本地坐标
        const pos = localPos.clone().sub(rectCenter)
        const v0 = {x: pos.x - w * 0.5, y: pos.y - h * 0.5}
        const v2 = {x: pos.x + w * 0.5, y: pos.y + h * 0.5}
        const box = new THREE.Box3()
        box.min.set(v0.x, v0.y, 0)
        box.max.set(v2.x, v2.y, 0)
        // 转换为canvas坐标 (0, 1)
        // 这里之所以要反转y，是因为在canvas中绘制时，使用的是屏幕坐标系，坐标原点位于屏幕左上角
        // uv贴图的坐标原点位于屏幕左下角，将屏幕坐标以 x轴旋转180度，就是uv贴图的坐标系
        const {min, max} = box
        const minX = min.x / size + 0.5
        const minY = 1.0 - (min.y / size + 0.5)
        const maxX = max.x / size + 0.5
        const maxY = 1.0 - (max.y / size + 0.5)
        min.set(minX, maxY, 0)
        max.set(maxX, minY, 0)
        item.box = box
      }
      target.push(item);
      item.spraying = this.interpolatedSprayFlags[index];
    })
    return target
  }
}
